/**
 * This file is released under the GNU General Public License.
 * Refer to the COPYING file distributed with this package.
 *
 * Copyright (c) 2008-2009 WURFL-Pro srl
 */
package net.sourceforge.wurfl.core;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

import net.sourceforge.wurfl.core.resource.ModelDevice;

import org.apache.commons.lang.Validate;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * This is a default Device implementation.
 * 
 * <p>
 * It uses a CapabilitiesHolder to retrieve capabilities values and
 * MarkupResolver to resolve MarkUp.
 * </p>
 * 
 * @author WURFL-PRO SRL, Rome, Italy
 * @version $Id: DefaultDevice.java 1045 2009-03-09 15:52:53Z filippo.deluca $
 */
public class DefaultDevice implements Device, Serializable {

	/** serial */
	private static final long serialVersionUID = 101L;

	// Private members ****************************************************

	/** Logger */
	private static final Log log = LogFactory.getLog(DefaultDevice.class);

	/** Device identifier */
	private final String id;

	private final String userAgent;

	/** CapabilitiesHolder instance used to get capabilities values */
	private final CapabilitiesHolder capabilityHolder;

	/** MarkupResolver used to obtain MarkUp for device */
	private final MarkupResolver markupResolver;

	/** It is not serialized */
	private transient MarkUp markUp;

	/**
	 * Build a Device.
	 * 
	 * @param root
	 *            The ModelDevice instance root of this {@link Device}.
	 * @param capabilitiesHolder
	 *            The CapabilitiesHolder used to get capabilities values.
	 * @param markupResolver
	 *            The MarkupResolver used to obtain MarkUp for this Device.
	 */
	public DefaultDevice(ModelDevice root,
			CapabilitiesHolder capabilitiesHolder, MarkupResolver markupResolver) {

		Validate.notNull(root, "The root device must be not null");
		Validate.notNull(capabilitiesHolder,
				"The capabilitiesHolder must be not null");
		Validate.notNull(markupResolver, "The markupResolver must be not null");

		this.id = root.getID();
		this.userAgent = root.getUserAgent();

		this.capabilityHolder = capabilitiesHolder;
		this.markupResolver = markupResolver;
	}

	/**
	 * {@inheritDoc}
	 */
	public String getId() {
		return id;
	}

	/**
	 * {@inheritDoc}
	 */
	public String getUserAgent() {
		return userAgent;
	}

	/**
	 * {@inheritDoc}
	 */
	public String getCapability(String name)
			throws CapabilityNotDefinedException {
		return this.capabilityHolder.getCapabilityValue(name);
	}

	/**
	 * {@inheritDoc}
	 */
	public Map getCapabilities() {

		Map capabilities = new HashMap(capabilityHolder.getAllCapabilities());
		return capabilities;
	}

	/**
	 * {@inheritDoc}
	 */
	public MarkUp getMarkUp() {

		// waste memory?
		if (markUp == null) {
			log.trace("Resolving MarkUp for device...");
			markUp = markupResolver.getMarkupForDevice(this);
		}
		return markUp;
	}

	// Commons methods ****************************************************

	/**
	 * {@inheritDoc}
	 * It assumes two DefaultDevices with equals root ModelDevices are equals.
	 */
	public boolean equals(Object obj) {

		EqualsBuilder eb = new EqualsBuilder();

		eb.appendSuper(getClass().isInstance(obj));

		if (eb.isEquals()) {
			DefaultDevice other = (DefaultDevice) obj;
			eb.append(id, other.id);
		}

		return eb.isEquals();
	}

	public int hashCode() {

		return new HashCodeBuilder(63,89).append(getClass()).append(id).toHashCode();
	}

	public String toString() {

		return new ToStringBuilder(this).append(id).toString();
	}

}
